using System;
using System.Globalization;

namespace GeneticAlgorithms.Alphabets
{
    /// <summary>
    /// Determines whether lowercase, uppercase of all cases should be used.
    /// </summary>
    public enum LetterCases
    {
        /// <summary>
        /// Use just lowercase letters.
        /// </summary>
        Lowercase,

        /// <summary>
        /// Use just uppercase letters.
        /// </summary>
        Uppercase,

        /// <summary>
        /// Any case can be used.
        /// </summary>
        All
    }
    
    /// <summary>
    /// An aplhabet composed of letters.
    /// </summary>
    public class LettersAlphabet : IAlphabet<char>
    {
        #region Constants, properties and fields

        /// <summary>
        /// The range for the alphabet.
        /// </summary>
        private int range;
        
        /// <summary>
        /// Helper constant for range calculation. 
        /// </summary>
        private const int Diff = ((int)'Z') - ((int)'A') + 1;

        /// <summary>
        /// First letter numeric value
        /// </summary>
        private readonly int firstVal;

        /// <summary>
        /// The possible case for the alphabet letters
        /// </summary>
        private LetterCases lettersCase;

        /// <summary>
        /// Gets or sets the letters case.
        /// </summary>
        /// <value>The letters case.</value>
        public LetterCases LettersCase
        {
            get { return lettersCase; }
        }

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="LettersAlphabet"/> class.
        /// </summary>
        /// <param name="lettersCase">The letters case.</param>
        public LettersAlphabet(LetterCases lettersCase)
        {
            this.lettersCase = lettersCase;
            if (lettersCase == LetterCases.All)
            {
                range = Diff * 2;
            }
            else
            {
                range = Diff;
            }

            if (lettersCase == LetterCases.Lowercase)
            {
                firstVal = (int)'a';
            }
            else
            {
                firstVal = (int)'A';
            }
        }

        #region IAlphabet<char> Members

        /// <summary>
        /// Gets the numeric range for the alphabet.
        /// </summary>
        /// <value>
        /// The total number of different values genes
        /// in this alphabet may have.
        /// </value>
        /// <remarks>
        /// This property is used for random gene generation.
        /// </remarks>
        public int Range
        {
            get 
            { 
                return range; 
            }
        }

        /// <summary>
        /// Converts from an int into a gene value.
        /// </summary>
        /// <param name="value">The numeric value to be converted.</param>
        /// <returns>
        /// A gene that corresponds to the numeric value.
        /// </returns>
        public char ConvertFromInt(int value)
        {
            if (value >= range)
                throw new ArgumentOutOfRangeException("value");

            if ((lettersCase == LetterCases.All) && (value >= Diff))
            {
                return Char.ToLower(
                    ConvertFromInt(value - Diff),
                    CultureInfo.InvariantCulture);
            }

            return (char)(value + firstVal);
        }

        #endregion
    }
}
